
import functools
import tkinter as tk


class PegCore:

    def __init__(self):
        self.setup()

    def setup(self):
        self.grid = [
            [1 if self.pos_valid(i, j) else 0
                for j in range(7)] for i in range(7)]
        self.grid[3][3] = -1
        self.opers = []

    def pos_valid(self, i, j):
        return 0 <= i and i < 7 and 0 <= j and j < 7 \
            and (i in (2, 3, 4) or j in (2, 3, 4))

    def jump(self, i, j, toi, toj):
        jumpi, jumpj = (i + toi) // 2, (j + toj) // 2
        if (
                self.pos_valid(i, j) and self.pos_valid(toi, toj)
                and self.grid[i][j] == 1 and self.grid[toi][toj] == -1
                and {abs(i - toi), abs(j - toj)} == {0, 2}
                and self.grid[jumpi][jumpj] == 1):
            self.grid[i][j] = -1
            self.grid[jumpi][jumpj] = -1
            self.grid[toi][toj] = 1
            self.opers.append((i, j, jumpi, jumpj, toi, toj))
            return True
        return False

    def undo(self):
        if self.opers:
            i, j, jumpi, jumpj, toi, toj = self.opers.pop()
            self.grid[i][j] = 1
            self.grid[jumpi][jumpj] = 1
            self.grid[toi][toj] = -1


class PegGUI(tk.Frame):

    def __init__(self, master):
        super().__init__(master)
        self.game = PegCore()
        self.clicki, self.clickj = None, None
        self.load_image()
        self.layout()
        self.update_buttongrid()

    def load_image(self):
        self.empty = tk.PhotoImage(file='empty.gif')
        self.piece = tk.PhotoImage(file='piece.gif')
        self.undo = tk.PhotoImage(file='undo.gif')
        self.reset = tk.PhotoImage(file='reset.gif')

    def layout(self):
        self.i_label = tk.Label(self, font=('Futura', 25, 'bold'))
        self.j_label = tk.Label(self, font=('Futura', 25, 'bold'))
        self.i_label.grid()
        self.j_label.grid(row=0, column=1)
        self.undo_button = tk.Button(
            self, width=45, height=45, image=self.undo,
            command=self.undo_handler)
        self.reset_button = tk.Button(
            self, width=45, height=45, image=self.reset,
            command=self.reset_handler)
        self.undo_button.grid(row=0, column=5)
        self.reset_button.grid(row=0, column=6)
        self.layout_buttongrid()

    def layout_buttongrid(self):
        self.buttongrid = []
        for i in range(7):
            self.buttongrid.append([])
            for j in range(7):
                if self.game.grid[i][j]:
                    button = tk.Button(
                        self, width=45, height=45,
                        command=functools.partial(self.click_handler, i, j))
                    button.grid(row=i, column=j)
                    self.buttongrid[-1].append(button)
                else:
                    self.buttongrid[-1].append(None)

    def update_buttongrid(self):
        for i in range(7):
            for j in range(7):
                if self.buttongrid[i][j]:
                    self.buttongrid[i][j].configure(
                        image=self.piece if self.game.grid[i][j] == 1
                        else self.empty)

    def click_handler(self, i, j):
        if self.clicki is not None and self.clickj is not None \
                and self.game.grid[i][j] == -1:
            self.game.jump(self.clicki, self.clickj, i, j)
            self.update_buttongrid()
            self.clicki, self.clickj = None, None
        elif self.game.grid[i][j] == 1:
            self.clicki, self.clickj = i, j
        self.i_label.configure(
            text=str(self.clicki) if self.clicki is not None else '')
        self.j_label.configure(
            text=str(self.clickj) if self.clickj is not None else '')

    def undo_handler(self, event=None):
        self.game.undo()
        self.update_buttongrid()

    def reset_handler(self, event=None):
        self.game.setup()
        self.update_buttongrid()


def setup_options(root, app):
    menu = tk.Menu(root)
    opers_menu = tk.Menu(menu)
    opers_menu.add_command(
        label='Undo', command=app.undo_handler, accelerator='Command-Z')
    opers_menu.add_command(
        label='Reset', command=app.reset_handler, accelerator='Command-R')
    menu.add_cascade(label='Operations', menu=opers_menu)
    root.configure(menu=menu)
    root.bind('<Command-z>', app.undo_handler)
    root.bind('<Command-r>', app.reset_handler)


def main():
    root = tk.Tk()
    root.title('Peg Solitaire')
    root.resizable(False, False)
    app = PegGUI(root)
    setup_options(root, app)
    app.grid()
    root.mainloop()


if __name__ == '__main__':
    main()
